package net.realsee.service.wcs.rcs.hik.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.realsee.common.constant.GclCcsConstants;
import net.realsee.common.constant.HikRcsConstants;
import net.realsee.common.constant.HttpStatus;
import net.realsee.common.enums.wcs.hik.HikCallbackMethodTypeEnum;
import net.realsee.common.enums.wcs.hik.HikRcsOperateErrorEnum;
import net.realsee.common.enums.wcs.intf.InterfaceCodeEnum;
import net.realsee.common.enums.wcs.intf.InterfaceResultCodeEnum;
import net.realsee.common.enums.wcs.mes.GclMesRunFlagEnum;
import net.realsee.common.enums.wcs.order.OrderProcessStatusEnum;
import net.realsee.common.enums.wcs.order.OrderStatusEnums;
import net.realsee.common.enums.wcs.order.OrderTypeEnums;
import net.realsee.common.exception.mes.GclMesTaskException;
import net.realsee.common.utils.InterfaceUtils;
import net.realsee.domain.wcs.intf.bo.InterfaceRequest;
import net.realsee.domain.wcs.intf.bo.InterfaceResponse;
import net.realsee.domain.wcs.rcs.hik.*;
import net.realsee.domain.wcs.warehouse.WcsWarehouseLocation;
import net.realsee.domain.wcs.work.WcsOrderDetail;
import net.realsee.domain.wcs.work.WcsOrderHeader;
import net.realsee.gcl.domain.bo.ccs.GclCcsLocationPosFixResult;
import net.realsee.gcl.domain.bo.ccs.GclCcsRequestParams;
import net.realsee.gcl.domain.bo.mes.GclMesOperateResult;
import net.realsee.gcl.domain.bo.mes.GclMesTaskStatusReportReq;
import net.realsee.gcl.service.IGclCcsService;
import net.realsee.gcl.service.IGclMesService;
import net.realsee.service.wcs.rcs.hik.IHikService;
import net.realsee.service.wcs.warehouse.IWcsWarehouseLocationService;
import net.realsee.service.wcs.work.IWcsOrderDetailService;
import net.realsee.service.wcs.work.IWcsOrderHeaderService;
import net.realsee.service.wcs.work.IWcsWarnService;
import org.springframework.stereotype.Service;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 海康RCS接口实现
 *
 * @author 孙正华
 * @since 2023/8/22 15:23
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class HikServiceImpl implements IHikService {

    private final IWcsOrderHeaderService orderHeaderService;
    private final IWcsOrderDetailService orderDetailService;
    private final IWcsWarehouseLocationService locationService;
    private final IGclMesService gclMesService;
    private final IGclCcsService gclCcsService;
    private final IWcsWarnService wcsWarnService;
    private final ScheduledExecutorService scheduledExecutorService;

    @Override
    public HikRcsOperateResult genAgvSchedulingTask(HikGenAgvSchedulingTaskReq request) {
        HikRcsOperateResult result = null;
        HttpResponse httpResponse = InterfaceUtils.callByHttp(
            InterfaceRequest.builder()
                .interfaceCode(InterfaceCodeEnum.HIK_RCS_GEN_TASK)
                .taskId(request.getTaskCode())
                .keyword1(request.getTaskCode())
                .bodyStr(JSONUtil.toJsonStr(request))
                .build());
        if (ObjectUtil.isNotNull(httpResponse) && httpResponse.isOk()) {
            result = JSONUtil.toBean(httpResponse.body(), HikRcsOperateResult.class);
        }
        return result;
    }

    @Override
    public HikRcsOperateResult continueTask(HikContinueTaskReq request) {
        HikRcsOperateResult result = null;
        HttpResponse httpResponse = InterfaceUtils.callByHttp(
            InterfaceRequest.builder()
                .interfaceCode(InterfaceCodeEnum.HIK_RCS_CONTINUE_TASK)
                .taskId(request.getTaskCode())
                .keyword1(request.getTaskCode())
                .bodyStr(JSONUtil.toJsonStr(request))
                .build());
        if (ObjectUtil.isNotNull(httpResponse) && httpResponse.isOk()) {
            result = JSONUtil.toBean(httpResponse.body(), HikRcsOperateResult.class);
        }
        return result;
    }

    @Override
    public HikRcsOperateResult cancelTask(HikCancelTaskReq request) {
        HikRcsOperateResult result = null;
        HttpResponse httpResponse = InterfaceUtils.callByHttp(
            InterfaceRequest.builder()
                .interfaceCode(InterfaceCodeEnum.HIK_RCS_CANCEL_TASK)
                .taskId(request.getTaskCode())
                .keyword1(request.getTaskCode())
                .bodyStr(JSONUtil.toJsonStr(request))
                .build());
        if (ObjectUtil.isNotNull(httpResponse) && httpResponse.isOk()) {
            result = JSONUtil.toBean(httpResponse.body(), HikRcsOperateResult.class);
        }
        return result;
    }

    @Override
    public HikRcsOperateResult setTaskPriority(HikTaskPrioritySetReq request) {
        HikRcsOperateResult result = null;
        String taskCode = CollUtil.getFirst(request.getPriorities()).getTaskCode();
        HttpResponse httpResponse = InterfaceUtils.callByHttp(
            InterfaceRequest.builder()
                .interfaceCode(InterfaceCodeEnum.HIK_RCS_SET_TASK_PRIORITY)
                .taskId(taskCode)
                .keyword1(taskCode)
                .bodyStr(JSONUtil.toJsonStr(request))
                .build());
        if (ObjectUtil.isNotNull(httpResponse) && httpResponse.isOk()) {
            result = JSONUtil.toBean(httpResponse.body(), HikRcsOperateResult.class);
        }
        return result;
    }

    /**
     * 任务执行回调
     */
    @Override
    public HikRcsOperateResult callback(HikRcsAgvCallback param) {
        // 海康RCS回调结果
        HikRcsOperateResult result = HikRcsOperateResult.builder()
            .code(HikRcsOperateErrorEnum.SUCCESS.getCode())
            .reqCode(param.getReqCode())
            .build();
        // 入站日志待更新对象
        InterfaceResponse inLogForUpdate = InterfaceResponse.builder()
            .taskId(param.getTaskCode())
            .keyword1(param.getTaskCode())
            .keyword2(param.getMethod())
            .content(JSONUtil.toJsonStr(result))
            .httpCode((long) HttpStatus.SUCCESS)
            .status(InterfaceResultCodeEnum.SUCCESS.getCode())
            .build();
        try {
            this.handleHikRcsCallback(param);
        } catch (Exception e) {
            // 异常时，设置错误信息
            result.setCode(HikRcsOperateErrorEnum.UNKNOWN_ERROR.getCode()).setData(e.getMessage());
            inLogForUpdate.setHttpCode((long) HttpStatus.ERROR).setStatus(InterfaceResultCodeEnum.FAILED.getCode()).setContent(JSONUtil.toJsonStr(result));
        } finally {
            // 更新入站日志内容
            InterfaceUtils.updateInlogByResponse(inLogForUpdate);
        }
        return result;
    }

    /**
     * 任务回调处理
     */
    private void handleHikRcsCallback(HikRcsAgvCallback param) {
        // 1.更新1020的单据头
        this.updateOrderHeader(param);
        // 2.新增1020的单据明细
        this.createOrderDetail(param);
        // 3.更新库位状态
        this.updateWarehouseLocationStatus(param);
        // 4.任务状态回传MES
        this.reportTaskStatusToMes(param);
        // 5.处理小车偏差逻辑
        this.handleContainerPosition(param);
    }

    /**
     * 更新单据头
     */
    private void updateOrderHeader(HikRcsAgvCallback param) {
        WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(param.getTaskCode(), OrderTypeEnums.UP_DOWN.getCode());
        orderHeader.setVehicleNo(param.getRobotCode());
        String method = param.getMethod();
        if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.START.getCode())) {
            // 任务开始
            orderHeader.setOrderStatus(OrderStatusEnums.STARTED.getCode());
            orderHeader.setProgressStatus(OrderProcessStatusEnum._1010020.getCode());
            orderHeader.setStartTime(DateUtil.date());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.WAITING.getCode())) {
            // 入库前等待校验（后续执行，需要定时任务拉起）
            orderHeader.setOrderStatus(OrderStatusEnums.HANG_UP.getCode());
            orderHeader.setProgressStatus(OrderProcessStatusEnum._1010010.getCode());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.END.getCode())) {
            // 任务全部完成
            orderHeader.setOrderStatus(OrderStatusEnums.COMPLETE.getCode());
            orderHeader.setProgressStatus(OrderProcessStatusEnum._1010110.getCode());
            orderHeader.setCompletionTime(DateUtil.date());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.CANCEL.getCode())) {
            // 任务取消
            orderHeader.setOrderStatus(OrderStatusEnums.CANCEL.getCode());
            orderHeader.setProgressStatus(OrderProcessStatusEnum._1010100.getCode());
            orderHeader.setCompletionTime(DateUtil.date());
        }
        orderHeaderService.updateById(orderHeader);
    }

    /**
     * 新增单据明细
     */
    private void createOrderDetail(HikRcsAgvCallback param) {
        WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(param.getTaskCode(), OrderTypeEnums.UP_DOWN.getCode());
        WcsOrderDetail orderDetail = BeanUtil.copyProperties(param, WcsOrderDetail.class);
        orderDetail.setOrderNo(param.getTaskCode());
        orderDetail.setOrderType(OrderTypeEnums.UP_DOWN.getCode());
        orderDetail.setInlineInputs(JSONUtil.toJsonStr(param));
        orderDetail.setTaskType(orderHeader.getTaskType());
        orderDetail.setStartTime(orderHeader.getStartTime());
        orderDetail.setCompletionTime(DateUtil.date());
        String method = param.getMethod();
        if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.START.getCode())) {
            // 任务开始
            orderDetail.setProgressStatus(OrderProcessStatusEnum._1010020.getCode());
            orderDetailService.save(orderDetail);
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.WAITING.getCode())) {
            // 入库前等待校验（后续执行，需要定时任务拉起）
            orderDetail.setProgressStatus(OrderProcessStatusEnum._1010010.getCode());
            // TODO 记录下一个点位等需要的信息

            orderDetailService.save(orderDetail);
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.END.getCode())) {
            // 任务全部完成
            orderDetail.setProgressStatus(OrderProcessStatusEnum._1010110.getCode());
            orderDetailService.save(orderDetail);
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.CANCEL.getCode())) {
            // 任务取消
            orderDetail.setProgressStatus(OrderProcessStatusEnum._1010100.getCode());
            orderDetailService.save(orderDetail);
        }
    }

    /**
     * 更新库位状态
     */
    private void updateWarehouseLocationStatus(HikRcsAgvCallback param) {
        String currentPositionCode = param.getCurrentPositionCode();
        String orderNo = param.getTaskCode();
        String method = param.getMethod();
        WcsWarehouseLocation warehouseLocation = locationService.getByMapMark(currentPositionCode);
        if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.IN_BIN.getCode(), HikCallbackMethodTypeEnum.OUT_BIN.getCode())) {
            warehouseLocation.setTaskCode(orderNo);
            warehouseLocation.setTaskRcsStatus(method);
            warehouseLocation.setTaskUpdateOn(DateUtil.date());
            locationService.updateById(warehouseLocation);
        }
    }

    /**
     * 当为扫货架码返回时，需要拿货架号去调MES接口进行下一步动作
     */
    private void reportTaskStatusToMes(HikRcsAgvCallback param) {
        String method = param.getMethod();
        if (StrUtil.equalsIgnoreCase(method, HikCallbackMethodTypeEnum.POD_SCAN.getCode())) {
            // 扫码成功
            this.podScan(param);
        } else if (StrUtil.equalsIgnoreCase(method, HikCallbackMethodTypeEnum.END.getCode())) {
            // 任务全部完成
            this.taskFinish(param);
        }
    }

    /**
     * 处理小车偏差逻辑
     */
    private void handleContainerPosition(HikRcsAgvCallback param) {
        if (StrUtil.equalsIgnoreCase(param.getMethod(), HikCallbackMethodTypeEnum.APPLY_FROM_AGV.getCode())) {
            WcsWarehouseLocation wcsWarehouseLocation = locationService.getByMapMark(param.getCurrentPositionCode());
            if (StrUtil.equalsIgnoreCase(GclCcsConstants.OK, wcsWarehouseLocation.getCcsPositionStatus())) {
                // 当RCS任务完成且CCS给出OK，则更新wcsPositionStatus字段为OK;
                wcsWarehouseLocation.setCcsPositionStatus(GclCcsConstants.OK);
            } else {
                // 当RCS任务完成且CCS给出NG，则更新wcsPositionStatus字段为NG&&触发计算并调RCS执行。
                wcsWarehouseLocation.setCcsPositionStatus(GclCcsConstants.NG);
                GclCcsRequestParams requestParams = new GclCcsRequestParams();
                requestParams.setX1_sen01_val(wcsWarehouseLocation.getX1Sen01Val());
                requestParams.setX1_sen02_val(wcsWarehouseLocation.getX1Sen02Val());
                requestParams.setY1_sen01_val(wcsWarehouseLocation.getY1Sen01Val());
                requestParams.setY1_sen02_val(wcsWarehouseLocation.getY1Sen02Val());
                requestParams.setLocation(wcsWarehouseLocation.getLocation());
                //GclCcsLocationPosFixResult angle = gclCcsService.getAngle(requestParams);
                // TODO 调用RCS
            }
            locationService.updateById(wcsWarehouseLocation);
        }
    }

    /**
     * 扫码check
     */
    private void podScan(HikRcsAgvCallback param) {
        WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(param.getTaskCode(), OrderTypeEnums.UP_DOWN.getCode());
        GclMesTaskStatusReportReq reportReq = new GclMesTaskStatusReportReq();
        reportReq.setId(param.getTaskCode());
        reportReq.setRunFlag(GclMesRunFlagEnum.TASK_CHECK.getCode());
        reportReq.setSnCode(StrUtil.isNotBlank(param.getPodCode()) ? param.getPodCode() : "NA");
        reportReq.setErrorCode(StrUtil.isNotBlank(param.getPodCode()) ? StrUtil.EMPTY : GclMesTaskException.CodeEnum.ER14.getCode());
        reportReq.setAgvNo(param.getRobotCode());
        reportReq.setMethod(orderHeader.getTaskType());
        GclMesOperateResult mesOperateResult = gclMesService.taskStatusReportToMes(reportReq);
        // TODO 是否更新库位状态为inbin

        if (ObjectUtil.isNotNull(mesOperateResult) && mesOperateResult.isSuccess()) {
            // mes确认通过，则继续执行RCS任务
            HikContinueTaskReq continueTaskReq = new HikContinueTaskReq();
            continueTaskReq.setReqCode(IdUtil.fastSimpleUUID());
            continueTaskReq.setTaskCode(param.getTaskCode());
            continueTaskReq.setReqTime(DateUtil.now());
            continueTaskReq.setClientCode(HikRcsConstants.CLIENT_CODE);
            HikRcsOperateResult hikRcsOperateResult = this.continueTask(continueTaskReq);

            if (ObjectUtil.isNotNull(hikRcsOperateResult) && hikRcsOperateResult.isSuccess()) {
                // 异步3秒后执行
                scheduledExecutorService.schedule(() -> {
                    reportReq.setRunFlag(GclMesRunFlagEnum.TASK_START.getCode());
                    gclMesService.taskStatusReportToMes(reportReq);
                }, 3, TimeUnit.SECONDS);
                // 更新库位状态为outbin
                HikRcsAgvCallback outBin = BeanUtil.copyProperties(param, HikRcsAgvCallback.class);
                outBin.setMethod(HikCallbackMethodTypeEnum.OUT_BIN.getCode());
                this.updateWarehouseLocationStatus(outBin);
            } else {
                // 海康继续任务执行失败，发给mes异常
                reportReq.setRunFlag(GclMesRunFlagEnum.TASK_WARN.getCode());
                reportReq.setErrorCode(GclMesTaskException.CodeEnum.UNKNOWN_ERR.getCode());
                reportReq.setMessage("上报货架号后,继续执行任务失败");
                gclMesService.taskStatusReportToMes(reportReq);
            }
        } else {
            // mes扫码确认失败后，直接取消任务
            HikCancelTaskReq hikCancelTaskReq = new HikCancelTaskReq();
            hikCancelTaskReq.setReqCode(IdUtil.fastSimpleUUID());
            hikCancelTaskReq.setReqTime(DateUtil.now());
            hikCancelTaskReq.setTaskCode(param.getTaskCode());
            hikCancelTaskReq.setClientCode(HikRcsConstants.CLIENT_CODE);
            hikCancelTaskReq.setForceCancel("0");
            //hikRpcService.cancelTask(hikCancelTaskReq);
            // 在此次调用RCS取消后，RCS取消完成后会继续回调更新任务状态为取消
        }
    }

    /**
     * 任务完成
     */
    private void taskFinish(HikRcsAgvCallback param) {
        WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(param.getTaskCode(), OrderTypeEnums.UP_DOWN.getCode());
        GclMesTaskStatusReportReq reportReq = new GclMesTaskStatusReportReq();
        reportReq.setId(param.getTaskCode());
        reportReq.setRunFlag(GclMesRunFlagEnum.TASK_FINISH.getCode());
        reportReq.setSnCode(StrUtil.isNotBlank(param.getPodCode()) ? param.getPodCode() : "NA");
        reportReq.setAgvNo(param.getRobotCode());
        reportReq.setMethod(orderHeader.getTaskType());
        gclMesService.taskStatusReportToMes(reportReq);
    }

    /**
     * 告警推送
     */
    @Override
    public HikRcsOperateResult warnCallback(HikWarnReq param) {
        // 海康RCS回调结果
        HikRcsOperateResult result = HikRcsOperateResult.builder()
            .code(HikRcsOperateErrorEnum.SUCCESS.getCode())
            .message(HikRcsOperateErrorEnum.SUCCESS.getMessage())
            .reqCode(param.getReqCode())
            .build();

        // 保存告警信息
        wcsWarnService.insertWarnData(param.getData());
        return result;
    }
}
